package com.lee.mq.mqclient;

import com.lee.mq.common.*;
import com.lee.mq.mqserver.core.BasicProperties;
import com.lee.mq.mqserver.core.ExchangeType;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lishuo
 * Date: 2023-10-31
 * Time: 11:40
 */
public class Channel {
    private String channelId;
    // 表示当前channel所属连接
    private Connection connection;
    // 这个哈希表用于储存后续客户端收到的服务器响应（key表示rid,value表示返回的响应信息）
    private ConcurrentHashMap<String, BasicReturns> basicReturnsMap = new ConcurrentHashMap<>();
    // 这里约定一个channel中只能存在一个Consumer
    private Consumer consumer;

    // 使用构造方法初始化Channel
    public Channel(String channelId,Connection connection) {
        this.channelId = channelId;
        this.connection = connection;
    }

    /**
     * 这个方法用来阻塞等待服务器的响应
     * @param rid
     * @return
     */
    private BasicReturns waitResult(String rid) {
        BasicReturns basicReturns = null;
        while ((basicReturns = basicReturnsMap.get(rid)) == null) {
            // 如果当前没对应的响应，则阻塞等待
            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        // 读取成功后，将basicReturnsMap中的响应对象删除
        basicReturnsMap.remove(rid);
        return basicReturns;
    }

    public void putReturns(BasicReturns basicReturns) {
        basicReturnsMap.put(basicReturns.getRid(),basicReturns);
        synchronized (this) {
            notifyAll();
        }
    }


    /**
     * 这个方法用于生成 rid
     * @return
     */
    private String generateRid() {
        return "R-"+ UUID.randomUUID().toString();
    }

    /**
     * 告诉服务器要创建一个channel
     * @return
     */
    public boolean createChannel() throws IOException {
        // 封装载荷部分
        BasicArguments arguments = new BasicArguments();
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(arguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0x1);
        request.setLength(payload.length);
        request.setPayload(payload);
        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(arguments.getRid());
        // 返回结果
        return basicReturns.isOk();
    }

    /**
     * 告诉数据库销毁一个channel
     * @return
     */
    public boolean closeChannel() throws IOException {
        // 封装载荷部分
        BasicArguments arguments = new BasicArguments();
        arguments.setChannelId(channelId);
        arguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(arguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0x2);
        request.setLength(payload.length);
        request.setPayload(payload);
        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(arguments.getRid());
        // 返回结果
        return basicReturns.isOk();
    }


    /**
     * 创建一个交换机
     * @param exchangeName
     * @param exchangeType
     * @param durable
     * @param autoDelete
     * @param arguments
     * @return
     * @throws IOException
     */
    public boolean exchangeDeclare(String exchangeName, ExchangeType exchangeType,boolean durable,boolean autoDelete,
                                   Map<String,Object> arguments) throws IOException {
        // 封装载荷部分
        ExchangeDeclareArguments exchangeArgumets = new ExchangeDeclareArguments();
        exchangeArgumets.setChannelId(channelId);
        exchangeArgumets.setRid(generateRid());
        exchangeArgumets.setExchangeName(exchangeName);
        exchangeArgumets.setExchangeType(exchangeType);
        exchangeArgumets.setDurable(durable);
        exchangeArgumets.setAutoDelete(autoDelete);
        exchangeArgumets.setArguments(arguments);
        byte[] payload = BinaryTool.toBytes(exchangeArgumets);
        // 封装请求对象
        Request request = new Request();
        request.setType(0x3);
        request.setLength(payload.length);
        request.setPayload(payload);
        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(exchangeArgumets.getRid());
        // 返回结果
        return basicReturns.isOk();
    }

    /**
     * 删除一个交换机
     * @param exchangeName
     * @return
     */
    public boolean exchangeDelete(String exchangeName) throws IOException {
        // 封装载荷部分
        ExchangeDeleteArguments exchangeArguments = new ExchangeDeleteArguments();
        exchangeArguments.setChannelId(channelId);
        exchangeArguments.setRid(generateRid());
        exchangeArguments.setExchangeName(exchangeName);
        byte[] payload = BinaryTool.toBytes(exchangeArguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0x4);
        request.setLength(payload.length);
        request.setPayload(payload);
        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(exchangeArguments.getRid());
        // 返回结果
        return basicReturns.isOk();
    }

    /**
     * 创建一个队列
     * @param queueName
     * @param durable
     * @param exclusive
     * @param autoDelete
     * @param argumets
     * @return
     */
    public boolean queueDeclare(String queueName,boolean durable,boolean exclusive,boolean autoDelete,
                                Map<String,Object> argumets) throws IOException {
        // 封装载荷部分
        QueueDeclareArguments queueDeclareArguments = new QueueDeclareArguments();
        queueDeclareArguments.setChannelId(channelId);
        queueDeclareArguments.setRid(generateRid());
        queueDeclareArguments.setQueueName(queueName);
        queueDeclareArguments.setDurable(durable);
        queueDeclareArguments.setExclusive(exclusive);
        queueDeclareArguments.setAutoDelete(autoDelete);
        queueDeclareArguments.setArguments(argumets);
        byte[] payload = BinaryTool.toBytes(queueDeclareArguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0x5);
        request.setLength(payload.length);
        request.setPayload(payload);

        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(queueDeclareArguments.getRid());
        // 返回结果
        return basicReturns.isOk();

    }

    /**
     * 删除一个队列
     * @param queueName
     * @return
     */
    public boolean queueDelete(String queueName) throws IOException {
        // 封装载荷部分
        QueueDeleteArguments queueDeleteArguments = new QueueDeleteArguments();
        queueDeleteArguments.setChannelId(channelId);
        queueDeleteArguments.setRid(generateRid());
        queueDeleteArguments.setQueueName(queueName);
        byte[] payload = BinaryTool.toBytes(queueDeleteArguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0x6);
        request.setLength(payload.length);
        request.setPayload(payload);

        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(queueDeleteArguments.getRid());
        // 返回结果
        return basicReturns.isOk();
    }

    /**
     * 创建一个绑定
     * @param exchangeName
     * @param queueName
     * @param bindingKey
     * @return
     */
    public boolean queueBind(String exchangeName,String queueName,String bindingKey) throws IOException {
        // 封装载荷部分
        QueueBindArguments queueBindArguments = new QueueBindArguments();
        queueBindArguments.setChannelId(channelId);
        queueBindArguments.setRid(generateRid());
        queueBindArguments.setQueueName(queueName);
        queueBindArguments.setExchangeName(exchangeName);
        queueBindArguments.setBindingKey(bindingKey);
        byte[] payload = BinaryTool.toBytes(queueBindArguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0x7);
        request.setLength(payload.length);
        request.setPayload(payload);
        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(queueBindArguments.getRid());
        // 返回结果
        return basicReturns.isOk();
    }

    /**
     * 删除一个绑定
     * @param exchangeName
     * @param queueName
     * @return
     */
    public boolean queueUnbind(String exchangeName,String queueName) throws IOException {
        // 封装载荷部分
        QueueUnbindArguments queueUnbindArguments = new QueueUnbindArguments();
        queueUnbindArguments.setChannelId(channelId);
        queueUnbindArguments.setRid(generateRid());
        queueUnbindArguments.setExchangeName(exchangeName);
        queueUnbindArguments.setQueueName(queueName);
        byte[] payload = BinaryTool.toBytes(queueUnbindArguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0x8);
        request.setLength(payload.length);
        request.setPayload(payload);

        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(queueUnbindArguments.getRid());
        // 返回结果
        return basicReturns.isOk();
    }

    /**
     * 发送消息
     * @param exchangeName
     * @param routingKey
     * @param basicProperties
     * @param body
     * @return
     */
    public boolean basicPublish(String exchangeName, String routingKey, BasicProperties basicProperties,byte[] body) throws IOException {
        // 封装载荷部分
        BasicPublishArguments basicPublishArguments = new BasicPublishArguments();
        basicPublishArguments.setChannelId(channelId);
        basicPublishArguments.setRid(generateRid());
        basicPublishArguments.setExchangeName(exchangeName);
        basicPublishArguments.setRoutingKey(routingKey);
        basicPublishArguments.setBasicProperties(basicProperties);
        basicPublishArguments.setBody(body);
        byte[] payload = BinaryTool.toBytes(basicPublishArguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0x9);
        request.setLength(payload.length);
        request.setPayload(payload);

        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(basicPublishArguments.getRid());
        // 返回结果
        return basicReturns.isOk();
    }

    /**
     * 订阅消息
     * @param queueName
     * @param autoAck
     * @param consumer
     * @return
     */
    public boolean basicConsume(String queueName,boolean autoAck,Consumer consumer) throws MqException, IOException {
        // 首先设置回调（一个channel中只能有一份回调）
        if (this.consumer != null) {
            throw new MqException("channel: " +channelId+"已经设置了回调，不能重复设置!");
        }
        this.consumer = consumer;

        // 封装载荷部分
        BasicConsumeArguments basicConsumeArguments = new BasicConsumeArguments();
        basicConsumeArguments.setChannelId(channelId);
        basicConsumeArguments.setRid(generateRid());
        basicConsumeArguments.setConsumerTag(channelId);
        basicConsumeArguments.setQueueName(queueName);
        basicConsumeArguments.setAutoAck(autoAck);
        byte[] payload = BinaryTool.toBytes(basicConsumeArguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0xa);
        request.setLength(payload.length);
        request.setPayload(payload);

        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(basicConsumeArguments.getRid());
        // 返回结果
        return basicReturns.isOk();
    }

    /**
     * 手动应答
     * @param queueName
     * @param messageId
     * @return
     */
    public boolean basicAck(String queueName,String messageId) throws IOException {
        // 封装载荷部分
        BasicAckArguments basicAckArguments = new BasicAckArguments();
        basicAckArguments.setChannelId(channelId);
        basicAckArguments.setRid(generateRid());
        basicAckArguments.setQueueName(queueName);
        basicAckArguments.setMessageId(messageId);
        byte[] payload = BinaryTool.toBytes(basicAckArguments);
        // 封装请求对象
        Request request = new Request();
        request.setType(0xb);
        request.setLength(payload.length);
        request.setPayload(payload);

        // 发送请求
        connection.writeRequest(request);
        // 等待服务器响应
        BasicReturns basicReturns = waitResult(basicAckArguments.getRid());
        // 返回结果
        return basicReturns.isOk();
    }

    public String getChannelId() {
        return channelId;
    }

    public void setChannelId(String channelId) {
        this.channelId = channelId;
    }

    public Connection getConnection() {
        return connection;
    }

    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    public ConcurrentHashMap<String, BasicReturns> getBasicReturnsMap() {
        return basicReturnsMap;
    }

    public void setBasicReturnsMap(ConcurrentHashMap<String, BasicReturns> basicReturnsMap) {
        this.basicReturnsMap = basicReturnsMap;
    }

    public Consumer getConsumer() {
        return consumer;
    }

    public void setConsumer(Consumer consumer) {
        this.consumer = consumer;
    }
}
